﻿using System;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

namespace DDOS
{
    public class InputManager
    {
        #region singleton properties
        private static InputManager inputManagerInstance;
        public static InputManager InputManagerInstance
        {
            get
            {
                if (inputManagerInstance == null)
                {
                    inputManagerInstance = new InputManager();
                }
                return inputManagerInstance;
            }
        }
        #endregion

        #region fields
        private const float THUMBSTICK_THRESHOLD = .75f;

        private KeyboardState previousKeyboardState;
        private KeyboardState currentKeyboardState;

        private MouseState previousMouseState;
        private MouseState currentMouseState;

        private GamePadState[] previousGamepadStates;
        private GamePadState[] currentGamepadStates;
        #endregion

        #region constructor
        public InputManager()
        {
            previousGamepadStates = new GamePadState[4];
            currentGamepadStates = new GamePadState[4];

            currentKeyboardState = Keyboard.GetState();
            currentMouseState = Mouse.GetState();

            for (int i = 0; i < 4; i++)
            {
                currentGamepadStates[0] = GamePad.GetState((PlayerIndex)i);
            }
        }
        #endregion

        #region methods
        public void Update()
        {
            previousKeyboardState = currentKeyboardState;
            currentKeyboardState = Keyboard.GetState();

            previousMouseState = currentMouseState;
            currentMouseState = Mouse.GetState();

            for (int i = 0; i < 4; i++)
            {
                previousGamepadStates[i] = currentGamepadStates[i];
                currentGamepadStates[i] = GamePad.GetState((PlayerIndex)i);
            }
        }

        #region keyboard methods
        public bool KeyIsDown(Keys key)
        {
            return currentKeyboardState.IsKeyDown(key);
        }

        public bool KeyIsUp(Keys key)
        {
            return currentKeyboardState.IsKeyUp(key);
        }

        public bool KeyWasPressed(Keys key)
        {
            return previousKeyboardState.IsKeyUp(key) && currentKeyboardState.IsKeyDown(key);
        }

        public bool KeyWasReleased(Keys key)
        {
            return previousKeyboardState.IsKeyDown(key) && currentKeyboardState.IsKeyUp(key);
        }
        #endregion

        #region mouse methods
        public ButtonState MouseButtonState(MouseButton mouseButton)
        {
            switch (mouseButton)
            {
                case MouseButton.Left:
                    return currentMouseState.LeftButton;
                case MouseButton.Right:
                    return currentMouseState.RightButton;
                case MouseButton.Middle:
                    return currentMouseState.MiddleButton;
                case MouseButton.XButton1:
                    return currentMouseState.XButton1;
                case MouseButton.XButton2:
                    return currentMouseState.XButton2;
                default:
                    return ButtonState.Released;
            }
        }

        public bool MouseButtonIsDown(MouseButton mouseButton)
        {
            return MouseButtonState(mouseButton) == ButtonState.Pressed;
        }

        public bool MouseButtonIsUp(MouseButton mouseButton)
        {
            return MouseButtonState(mouseButton) == ButtonState.Released;
        }

        public bool MouseButtonWasPressed(MouseButton mouseButton)
        {
            switch (mouseButton)
            {
                case MouseButton.Left:
                    return previousMouseState.LeftButton == ButtonState.Released && currentMouseState.LeftButton == ButtonState.Pressed;
                case MouseButton.Right:
                    return previousMouseState.RightButton == ButtonState.Released && currentMouseState.RightButton == ButtonState.Pressed;
                case MouseButton.Middle:
                    return previousMouseState.MiddleButton == ButtonState.Released && currentMouseState.MiddleButton == ButtonState.Pressed;
                case MouseButton.XButton1:
                    return previousMouseState.XButton1 == ButtonState.Released && currentMouseState.XButton1 == ButtonState.Pressed;
                case MouseButton.XButton2:
                    return previousMouseState.XButton2 == ButtonState.Released && currentMouseState.XButton2 == ButtonState.Pressed;
                default:
                    return false;
            }
        }

        public bool MouseButtonWasReleased(MouseButton mouseButton)
        {
            switch (mouseButton)
            {
                case MouseButton.Left:
                    return previousMouseState.LeftButton == ButtonState.Pressed && currentMouseState.LeftButton == ButtonState.Released;
                case MouseButton.Right:
                    return previousMouseState.RightButton == ButtonState.Pressed && currentMouseState.RightButton == ButtonState.Released;
                case MouseButton.Middle:
                    return previousMouseState.MiddleButton == ButtonState.Pressed && currentMouseState.MiddleButton == ButtonState.Released;
                case MouseButton.XButton1:
                    return previousMouseState.XButton1 == ButtonState.Pressed && currentMouseState.XButton1 == ButtonState.Released;
                case MouseButton.XButton2:
                    return previousMouseState.XButton2 == ButtonState.Pressed && currentMouseState.XButton2 == ButtonState.Released;
                default:
                    return false;
            }
        }

        public Point CurrentMousePosition
        {
            get
            {
                return new Point(currentMouseState.X, currentMouseState.Y);
            }
        }

        public Point PreviousMousePosition
        {
            get
            {
                return new Point(previousMouseState.X, previousMouseState.Y);
            }
        }

        public Point MousePositionDelta
        {
            get
            {
                return new Point(currentMouseState.X - previousMouseState.X, currentMouseState.Y - previousMouseState.Y);
            }
        }

        public bool MousePositionHasChanged
        {
            get
            {
                return previousMouseState.X != currentMouseState.X || previousMouseState.Y != currentMouseState.Y;
            }
        }

        #endregion

        #region gamepad methods
        public bool IsGamePadConnected(PlayerIndex player)
        {
            return currentGamepadStates[(int)player].IsConnected;
        }
        public bool IsGamePadConnected()
        {
            PlayerIndex player = PlayerIndex.One;
            return currentGamepadStates[(int)player].IsConnected;
        }

        public bool AnyGamePadButtonWasPressed(PlayerIndex player)
        {
            return GamePadButtonWasPressed(player, Buttons.A) || GamePadButtonWasPressed(player, Buttons.B) ||
                    GamePadButtonWasPressed(player, Buttons.X) || GamePadButtonWasPressed(player, Buttons.Y) ||
                    GamePadButtonWasPressed(player, Buttons.DPadDown) || GamePadButtonWasPressed(player, Buttons.DPadLeft) ||
                    GamePadButtonWasPressed(player, Buttons.DPadRight) || GamePadButtonWasPressed(player, Buttons.DPadUp);
        }
        public bool AnyGamePadButtonWasPressed()
        {
            PlayerIndex player = PlayerIndex.One;
            return GamePadButtonWasPressed(player, Buttons.A) || GamePadButtonWasPressed(player, Buttons.B) ||
                    GamePadButtonWasPressed(player, Buttons.X) || GamePadButtonWasPressed(player, Buttons.Y) ||
                    GamePadButtonWasPressed(player, Buttons.DPadDown) || GamePadButtonWasPressed(player, Buttons.DPadLeft) ||
                    GamePadButtonWasPressed(player, Buttons.DPadRight) || GamePadButtonWasPressed(player, Buttons.DPadUp);
        }

        public bool IsGamePadButtonUp(PlayerIndex player, Buttons button)
        {
            return currentGamepadStates[(int)player].IsButtonUp(button);
        }
        public bool IsGamePadButtonUp(Buttons button)
        {
            PlayerIndex player = PlayerIndex.One;
            return currentGamepadStates[(int)player].IsButtonUp(button);
        }

        public bool IsGamePadButtonDown(PlayerIndex player, Buttons button)
        {
            return currentGamepadStates[(int)player].IsButtonDown(button);
        }
        public bool IsGamePadButtonDown(Buttons button)
        {
            PlayerIndex player = PlayerIndex.One;
            return currentGamepadStates[(int)player].IsButtonDown(button);
        }

        public bool GamePadButtonWasPressed(PlayerIndex player, Buttons button)
        {
            return previousGamepadStates[(int)player].IsButtonUp(button) && currentGamepadStates[(int)player].IsButtonDown(button);
        }
        public bool GamePadButtonWasPressed(Buttons button)
        {
            PlayerIndex player = PlayerIndex.One;
            return previousGamepadStates[(int)player].IsButtonUp(button) && currentGamepadStates[(int)player].IsButtonDown(button);
        }

        public bool GamePadButtonWasReleased(PlayerIndex player, Buttons button)
        {
            return previousGamepadStates[(int)player].IsButtonDown(button) && currentGamepadStates[(int)player].IsButtonUp(button);
        }
        public bool GamePadButtonWasReleased(Buttons button)
        {
            PlayerIndex player = PlayerIndex.One;
            return previousGamepadStates[(int)player].IsButtonDown(button) && currentGamepadStates[(int)player].IsButtonUp(button);
        }

        public Vector2 GetGamePadThumbstickState(PlayerIndex player, GamePadThumbstick thumbstick)
        {
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    {
                        Vector2 retVal = currentGamepadStates[(int)player].ThumbSticks.Left;
                        retVal.Y = -retVal.Y;
                        return retVal;
                    }
                case GamePadThumbstick.right:
                    {
                        Vector2 retVal = currentGamepadStates[(int)player].ThumbSticks.Right;
                        retVal.Y = -retVal.Y;
                        return retVal;
                    }
                default:
                    return new Vector2(-1f, -1f);
            }
        }
        public Vector2 GetGamePadThumbstickState(GamePadThumbstick thumbstick)
        {
            PlayerIndex player = PlayerIndex.One;
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    {
                        Vector2 retVal = currentGamepadStates[(int)player].ThumbSticks.Left;
                        retVal.Y = -retVal.Y;
                        return retVal;
                    }
                case GamePadThumbstick.right:
                    {
                        Vector2 retVal = currentGamepadStates[(int)player].ThumbSticks.Right;
                        retVal.Y = -retVal.Y;
                        return retVal;
                    }
                default:
                    return new Vector2(-1f, -1f);
            }
        }

        public bool GamePadThumbstickMovedUp(PlayerIndex player, GamePadThumbstick thumbstick)
        {
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return previousGamepadStates[(int)player].ThumbSticks.Left.Y > THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Left.Y <= THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X);
                case GamePadThumbstick.right:
                    return previousGamepadStates[(int)player].ThumbSticks.Right.Y > THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Right.Y <= THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X);
                default:
                    return false;
            }
        }
        public bool GamePadThumbstickMovedUp(GamePadThumbstick thumbstick)
        {
            PlayerIndex player = PlayerIndex.One;
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return previousGamepadStates[(int)player].ThumbSticks.Left.Y > THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Left.Y <= THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X);
                case GamePadThumbstick.right:
                    return previousGamepadStates[(int)player].ThumbSticks.Right.Y > THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Right.Y <= THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X);
                default:
                    return false;
            }
        }

        public bool GamePadThumbstickMovedDown(PlayerIndex player, GamePadThumbstick thumbstick)
        {
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return previousGamepadStates[(int)player].ThumbSticks.Left.Y < -THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Left.Y >= -THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X);
                case GamePadThumbstick.right:
                    return previousGamepadStates[(int)player].ThumbSticks.Right.Y < -THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Right.Y >= -THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X);
                default:
                    return false;
            }
        }
        public bool GamePadThumbstickMovedDown(GamePadThumbstick thumbstick)
        {
            PlayerIndex player = PlayerIndex.One;
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return previousGamepadStates[(int)player].ThumbSticks.Left.Y < -THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Left.Y >= -THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X);
                case GamePadThumbstick.right:
                    return previousGamepadStates[(int)player].ThumbSticks.Right.Y < -THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Right.Y >= -THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X);
                default:
                    return false;
            }
        }

        public bool GamePadThumbstickMovedRight(PlayerIndex player, GamePadThumbstick thumbstick)
        {
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return previousGamepadStates[(int)player].ThumbSticks.Left.X < THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Left.X >= THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y);
                case GamePadThumbstick.right:
                    return previousGamepadStates[(int)player].ThumbSticks.Right.X < THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Right.X >= THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y);
                default:
                    return false;
            }
        }
        public bool GamePadThumbstickMovedRight(GamePadThumbstick thumbstick)
        {
            PlayerIndex player = PlayerIndex.One;
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return previousGamepadStates[(int)player].ThumbSticks.Left.X < THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Left.X >= THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y);
                case GamePadThumbstick.right:
                    return previousGamepadStates[(int)player].ThumbSticks.Right.X < THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Right.X >= THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y);
                default:
                    return false;
            }
        }

        public bool GamePadThumbstickMovedLeft(PlayerIndex player, GamePadThumbstick thumbstick)
        {
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return previousGamepadStates[(int)player].ThumbSticks.Left.X > -THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Left.X <= -THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y);
                case GamePadThumbstick.right:
                    return previousGamepadStates[(int)player].ThumbSticks.Right.X > -THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Right.X <= -THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y);
                default:
                    return false;
            }
        }
        public bool GamePadThumbstickMovedLeft(GamePadThumbstick thumbstick)
        {
            PlayerIndex player = PlayerIndex.One;
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return previousGamepadStates[(int)player].ThumbSticks.Left.X > -THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Left.X <= -THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y);
                case GamePadThumbstick.right:
                    return previousGamepadStates[(int)player].ThumbSticks.Right.X > -THUMBSTICK_THRESHOLD && currentGamepadStates[(int)player].ThumbSticks.Right.X <= -THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X) > Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y);
                default:
                    return false;
            }
        }

        public bool GamePadThumbstickMoved(PlayerIndex player, GamePadThumbstick thumbstick)
        {
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return (Math.Abs(previousGamepadStates[(int)player].ThumbSticks.Left.X) < THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X) >= THUMBSTICK_THRESHOLD) || (Math.Abs(previousGamepadStates[(int)player].ThumbSticks.Left.Y) < THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y) >= THUMBSTICK_THRESHOLD);
                case GamePadThumbstick.right:
                    return (Math.Abs(previousGamepadStates[(int)player].ThumbSticks.Right.X) < THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X) >= THUMBSTICK_THRESHOLD) || (Math.Abs(previousGamepadStates[(int)player].ThumbSticks.Right.Y) < THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y) >= THUMBSTICK_THRESHOLD);
                default:
                    return false;
            }
        }
        public bool GamePadThumbstickMoved(GamePadThumbstick thumbstick)
        {
            PlayerIndex player = PlayerIndex.One;
            switch (thumbstick)
            {
                case GamePadThumbstick.left:
                    return (Math.Abs(previousGamepadStates[(int)player].ThumbSticks.Left.X) < THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.X) >= THUMBSTICK_THRESHOLD) || (Math.Abs(previousGamepadStates[(int)player].ThumbSticks.Left.Y) < THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Left.Y) >= THUMBSTICK_THRESHOLD);
                case GamePadThumbstick.right:
                    return (Math.Abs(previousGamepadStates[(int)player].ThumbSticks.Right.X) < THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.X) >= THUMBSTICK_THRESHOLD) || (Math.Abs(previousGamepadStates[(int)player].ThumbSticks.Right.Y) < THUMBSTICK_THRESHOLD && Math.Abs(currentGamepadStates[(int)player].ThumbSticks.Right.Y) >= THUMBSTICK_THRESHOLD);
                default:
                    return false;
            }
        }

        public float GetGamePadTriggerState(PlayerIndex player, GamePadTrigger trigger)
        {
            switch (trigger)
            {
                case GamePadTrigger.left:
                    return currentGamepadStates[(int)player].Triggers.Left;
                case GamePadTrigger.right:
                    return currentGamepadStates[(int)player].Triggers.Right;
                default:
                    return -1f;
            }
        }
        public float GetGamePadTriggerState(GamePadTrigger trigger)
        {
            PlayerIndex player = PlayerIndex.One;
            switch (trigger)
            {
                case GamePadTrigger.left:
                    return currentGamepadStates[(int)player].Triggers.Left;
                case GamePadTrigger.right:
                    return currentGamepadStates[(int)player].Triggers.Right;
                default:
                    return -1f;
            }
        }
        #endregion


        #endregion

        #region enumerations
        public enum MouseButton
        {
            Left, Right, Middle, XButton1, XButton2
        }

        public enum GamePadThumbstick
        {
            left, right
        }

        public enum GamePadTrigger
        {
            left, right
        }
        #endregion
    }
}